﻿namespace Groundwork.Extensions
{
    using System;
    using System.Collections.Generic;
    using System.Text;
    using Groundwork.Core;
    using System.IO;

    /// <summary>
    /// String Extensions
    /// </summary>
    public static class Strings
    {

        /// <summary>
        /// Generates a string containing Lorum Ipsum text
        /// </summary>
        /// <param name="value">string</param>
        /// <param name="minimumWords">Minimum number of words</param>
        /// <param name="maximumWords">Maximum number of words</param>
        /// <param name="minimumSentences">Minimum number of sentences</param>
        /// <param name="maximumSentences">Maximum number of sentences</param>
        /// <param name="numberOfParagraphs">Number of paragraphs</param>
        /// <returns>Random string containing Lorum Ipsum text</returns>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// string lorumIpsum = "".LorumIpsum(10, 20, 2, 2);
        /// ]]></code></example>
        public static string LorumIpsum(this string value, int minimumWords, int maximumWords, int minimumSentences, int maximumSentences, int numberOfParagraphs)
        {
            string localText = StaticUtility.GetEmbeddedData("LorumIpsum.txt", "Data");

            var rand = new Random();
            int numberOfSentences = rand.Next(maximumSentences - minimumSentences) + minimumSentences + 1;
            int numberOfWords = rand.Next(maximumWords - minimumWords) + minimumWords + 1;
            string[] words = localText.Split(';');
            
            StringBuilder lorum = new StringBuilder();
            for (int p = 0; p < numberOfParagraphs; p++)
            {
                for (int s = 0; s < numberOfSentences; s++)
                {
                    for (int w = 0; w < numberOfWords; w++)
                    {
                        lorum.Append(string.Concat(words[rand.Next(words.Length)], " "));
                    }
                
                    lorum.Append(". ");
                }
            
                lorum.Append(Environment.NewLine + Environment.NewLine);
            }

            return lorum.ToString();
        }

        /// <summary>
        /// Checks to see if the string array contains a specific value
        /// </summary>
        /// <param name="value">current string array</param>
        /// <param name="valueToFind">value to find</param>
        /// <returns>true if the value is found</returns>
        public static bool Contains(this string[] value, string valueToFind)
        {
            bool found = false;
            foreach (string item in value)
            {
                found = item.ToLower() == valueToFind.ToLower();
                if (found) 
                { 
                    break; 
                }
            }
            return found;
        }


        /// <summary>
        /// Converts a string array into a string list.
        /// </summary>
        /// <param name="value">current string array</param>
        /// <returns>List{System.String}.</returns>
        public static List<string> ToList(this string[] value)
        {
            List<string> tmpList = new List<string>();
            value.ForEach((o) => { tmpList.Add((string)o); });
            return tmpList;
        }

        /// <summary>
        /// converts a list of strings into a delimited string
        /// </summary>
        /// <param name="value">current list to convert</param>
        /// <param name="itemDelimiter">The item delimiter.</param>
        /// <returns>List converted to a delimited string using the specified item delimiter</returns>
        /// <exception cref="System.ArgumentNullException">Throws if the delimiter is null or empty</exception>
        public static string ToDelimitedString(this List<string> value, string itemDelimiter)
        {
            itemDelimiter.ThrowIfNull("itemDelimiter");
            StringBuilder builder = new StringBuilder();
            value.ForEach((o) => { builder.Append(string.Concat(o, itemDelimiter)); });
            return builder.ToString();
        }


        /// <summary>
        /// Formats the string provided using the provided object array
        /// </summary>
        /// <param name="format">string to format</param>
        /// <param name="args">object array to use </param>
        /// <returns>formatted string</returns>
        public static string Format(this string format, params object[] args)
        {
            if (args.IsNull()) 
            {
                return format;
            }
            
            try
            {
                return string.Format(format, args);
            }
            catch
            {
                throw;
            }
        }
        
        /// <summary>
        /// Determines if the string provided is null or empty
        /// </summary>
        /// <param name="value">value to check</param>
        /// <returns>true indicates the value as null, false indicates the value was provided</returns>
        public static bool IsNullOrEmpty(this string value)
        {
            if (string.IsNullOrEmpty(value))
            {
                return true;
            }

            return false;
        }
        
        /// <summary>
        /// Matches the value against (converted to lowercase) to the match value (converted to lowercase)
        /// </summary>
        /// <param name="value">current string</param>
        /// <param name="match">string to match</param>
        /// <returns>True indicates that the strings are the same</returns>
        public static bool IsSame(this string value, string match)
        {
        	if (value.ToLowerInvariant() == match.ToLowerInvariant())
        	{
        		return true;    	
        	}
        	return false;
        }
        
        
        

        /// <summary>
        /// Converts a delimited string into a Dictionary
        /// </summary>
        /// <typeparam name="T">dictionary key type</typeparam>
        /// <typeparam name="R">dictionary value type</typeparam>
        /// <param name="value">string to convert</param>
        /// <param name="keyValueDelimiter">delimiter used for key value separation</param>
        /// <param name="itemDelimiter">delimiter used for item separation</param>
        /// <returns>Dictionary in specified format</returns>
        public static Dictionary<T, R> ToDictionary<T, R>(this string value, char keyValueDelimiter, char itemDelimiter)
            where T : IConvertible
            where R : IConvertible
        {
            Dictionary<T, R> tempDictionary = new Dictionary<T, R>();
            string[] itemsArray = value.Split(itemDelimiter);

            foreach (var item in itemsArray)
            {
                string[] itemParts = ((string)item).Split(keyValueDelimiter);
                string testPartOne = itemParts[0]; 
                string testPartTwo = itemParts[1];
                if (itemParts[0] != null)
                {
                    testPartOne = itemParts[0];
                }

                if (itemParts[1] != null)
                {
                    testPartTwo = itemParts[1];
                } 

                T itemOne = default(T);
                R itemTwo = default(R);
                Objects.TryConvert<T>(testPartOne, out itemOne);
                Objects.TryConvert<R>(testPartTwo, out itemTwo);

                tempDictionary.Add(itemOne, itemTwo);
            }

            return tempDictionary;
        }


        /// <summary>
        /// Converts the current string into a specific type list.
        /// </summary>
        /// <typeparam name="T">Type of item to convert to</typeparam>
        /// <param name="value">The value.</param>
        /// <param name="itemDelimiter">The item delimiter.</param>
        /// <returns>Returns a list of T</returns>
        /// <remarks>
        /// It is possible to convert a list of numbers into a List of integers or longs etc..
        /// </remarks>
        /// <example>
        /// <code>
        /// <![CDATA[
        /// string myString = "1,2,3,4,5,6,7";
        /// List<int> outputList = myString.ToList<int>(',');
        /// ]]></code></example>
        public static List<T> ToList<T>(this string value, char itemDelimiter) where T : IConvertible
        {
            List<T> tempList = new List<T>();
            string[] itemsArray = value.Split(itemDelimiter);
            itemsArray.ForEach(i => 
            {
                if (!string.IsNullOrEmpty((string)i))
                {
                    T itemConverted = default(T);
                    Objects.TryConvert<T>(i, out itemConverted);
                    tempList.Add(itemConverted);
                }
            });            
            return tempList;
        }

        /// <summary>
        /// Converts a string into a stream
        /// </summary>
        /// <param name="value">string to convert</param>
        /// <returns>converted stream</returns>
        public static Stream ToStream(this string value)
        {
            Stream stream = new MemoryStream();
            byte[] byteTmp = Encoding.UTF8.GetBytes(value);
            stream.Write(byteTmp, 0, byteTmp.Length);
            return stream;
        }

        /// <summary>
        /// Readers a given stream and outputs a byte array of the input stream
        /// </summary>
        /// <param name="stream">stream to read</param>
        /// <returns>stream converted to a byte array</returns>
        public static byte[] ReadToEnd(Stream stream)
        {
            long originalPosition = stream.Position;
            stream.Position = 0;

            try
            {
                byte[] readBuffer = new byte[4096];

                int totalBytesRead = 0;
                int bytesRead;

                while ((bytesRead = stream.Read(readBuffer, totalBytesRead, readBuffer.Length - totalBytesRead)) > 0)
                {
                    totalBytesRead += bytesRead;

                    if (totalBytesRead == readBuffer.Length)
                    {
                        int nextByte = stream.ReadByte();
                        if (nextByte != -1)
                        {
                            byte[] temp = new byte[readBuffer.Length * 2];
                            Buffer.BlockCopy(readBuffer, 0, temp, 0, readBuffer.Length);
                            Buffer.SetByte(temp, totalBytesRead, (byte)nextByte);
                            readBuffer = temp;
                            totalBytesRead++;
                        }
                    }
                }

                byte[] buffer = readBuffer;
                if (readBuffer.Length != totalBytesRead)
                {
                    buffer = new byte[totalBytesRead];
                    Buffer.BlockCopy(readBuffer, 0, buffer, 0, totalBytesRead);
                }

                return buffer;
            }
            finally
            {
                stream.Position = originalPosition;
            }
        }


    }
}
